In
some cases, we may want a plugin method to change which DOM elements
are referenced by the jQuery object. For example, suppose we wanted to
add a DOM traversal method that found the grandparents of the matched
elements:
jQuery.fn.grandparent = function() {
plug-ins, developingDOM traversal method, addingvar grandparents = [];
this.each(function() {
grandparents.push(this.parentNode.parentNode);
});
grandparents = jQuery.unique(grandparents);
return this.setArray(grandparents);
};
This method creates a new grandparents array, populating it by iterating over all of the elements currently referenced by the jQuery object. The standard DOM .parentNode property is used to find the grandparent elements, which are pushed onto the grandparents array. This array is stripped of its duplicates with a call to $.unique(). Then the internal jQuery .setArray()
method changes the set of matched elements to the new array. Now, we
can find and operate on the grandparent of an element with a single
method call.
To test our method, we'll set up a deeply-nested<div> structure:
<div>Deserunt mollit anim id est laborum</div>
<div>Ut enim ad minim veniam
<div>Quis nostrud exercitation
<div>Ullamco laboris nisi
<div>Ut aliquip ex ea</div>
<div class="target">Commodo consequat
<div>Lorem ipsum dolor sit amet</div>
</div>
</div>
</div>
plug-ins, developingDOM traversal method, adding<div>Duis aute irure dolor</div>
<div>In reprehenderit
<div>In voluptate</div>
<div>Velit esse
<div>Cillum dolore</div>
<div class="target">Fugiat nulla pariatur</div>
</div>
<div>Excepteur sint occaecat cupidatat</div>
</div>
</div>
<div>Non proident</div>
</div>
<div>Sunt in culpa qui officia</div>
We'll identify the target elements (<div class="target">) by styling their text bold:
Now we can locate the items' grandparent elements by using our new method:
$(document).ready(function() {
$('.target').grandparent().addClass('highlight');
});
The highlight class correctly italicizes both grandparent items on the page:
However, this method is destructive.
The actual jQuery object is modified as a side effect—one
that becomes evident if we store the jQuery object in a variable:
$(document).ready(function() {
DOM traversal method, addingside effectvar $target = $('.target');
$target.grandparent().addClass('highlight');
$target.hide();
});
This code should highlight
the grandparent elements, and then hide the target elements. However,
the actual effect is that the grandparents are hidden instead:
The jQuery object stored in $target has changed to refer to the grandparent. To avoid this, we need to make the method nondestructive. This is made possible by the internal stack jQuery keeps for each object.
jQuery.fn.grandparent = function() {
DOM traversal method, addinginternal stack jQuery usedvar grandparents = [];
this.each(function() {
grandparents.push(this.parentNode.parentNode);
});
grandparents = jQuery.unique(grandparents);
return this.pushStack(grandparents);
};
By calling .pushStack() instead of .setArray(), we create a new jQuery object, rather than modifying the old one. Now the $target object is not modified, and the original target objects are hidden by our code:
As a side benefit, .pushStack() also allows the .end() and .andSelf() methods to work with our plugin, so we can chain methods together properly:
$(document).ready(function() {
$('.target').grandparent().andSelf().addClass('highlight');
});
DOM traversal methods such as .children() were destructive operations in jQuery 1.0, but became non-destructive in 1.1.